
/*
timerInterrupt_Match.pde  Copyright (c) 2010 Ed Bennett 

demo of setup for a compare-match timer2 interrupt for an Arduino AVR. Generates a square wave with a period of about 30uS -- at the expense of everything else.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


// timerInterrupt_Match.pde  Ed Bennett <ed@kineticsandelectron ics.com> 
// demo of setup for a compare-match timer2 interrupt for an Arduino AVR.
// Generates a square wave with a period of about 30uS -- at the expense of 
// everything else
// 8-30-10

// for an example of a timer overflow interrupt, see timerInterrupt_Overflow.pde


//
// syntax note: WGM22:0 means a group of 3 bits (2, 1, and 0) in the byte called WGM2. 
//


int HF0 = 13;       
int outval = 0;

void setup()
{  
  pinMode(HF0, OUTPUT); 
  IRQ_init();
}


void loop()
{   

}

/*

This function does setup on the timer 2 hardware to make it generate interrupts. The code uses 
output-compare channel A (it does not touch channel B) to fire the interrupt.

In this setup/initialization function, I'm poking the control bits into the appropriate 
hardware regisers in the MCU. Against normal practice, I'm poking hard-coded values (numbers)
into the registers. This is to make it easy to see what bit is doing what in the different 
registers. An example:

For the Mega48, 88, 168, and 328, bit "OCIE2A" (Output Compare Interrupt Enable timer 2 channel A)
is in bit position 1 of the TMSK2 register. To set this bit, the hard-coded version looks like
TIMSK2 |= 2, binary b00000010. Normally, the bit positions in registers would be expressed
using_BV() and mnemonics from the include file (iom168p.h) for the chip being used. In the include
file, OCIE2A is defined as 2. It's used like this: TIMSK2 |= _BV(OCIE2A); This makes code portable
across chips where OCIE2A might be in a different position in the register. For instance, on some
other chip OCIE2A might be in position 4 (binary b00010000, 0x10hex, decimal 16). The code 
TIMSK2 |= _BV(OCIE2A) would compile for the other chip, without modification, because the header
file for the other chip would have the correct value for OCIE2A (the value 4). By contrast, to run
on the other chip, the hard-coded value (0x02)used in this piece of code TMSK2 |= 2 would have to
be changed by hand to TIMSK2 |= 16. And you would have to read the register description for the
other chip to know this. This function is written using hard-coded values because it has a
didactic purpose in addition to a functional one. It's an opportunity to see what's happening
under the hood when you're poking registers.

*/ 




void IRQ_init(void)
{
  
// This is the master speed adjuster. (in addition to the prescaler, below) 
// It's a numeric value to match w/ TCNT2 to gen. an IRQ.
    OCR2A = 1;     



/* 
Setup  timer 2 for CTC (Clear Timer on Compare match) mode:
Set WGM22:0 = 0x07 (binary 111). (WGM = Waveform Generation Mode)

From the manual: "TOP is defined as OCR2A when WGM2:0 = 7". WGM21 and WGM20 are located at bit
positions 1 and 0 in TCCR2A. Bit WGM22 is located at bit position 3 in TCCR2B. ICK!

WGM value = 0x03 (111 binary) sets CTC mode to have the settings of: Fast PWM, OCR2A updated on
BOTTOM, and TOV set on TOP. Note that CTC mode requires only WGM21 to be set, but the timer
generates interrupts faster with WGM22:0 set.

Output pins are in "normal" mode i.e. disconnected from the timer, when COM2A1:0 = 0x00 and
COM2B1:0 = 0x00. This is the default value at startup.

*/
    TCCR2A |= 3; // binary 00000011
    TCCR2B |= 8; // binary 00001000



/*
Set the clock prescaler to "NO PRESCALING". With a 16MHz crystal,
TIMER2 is clocked at 16MHz. This setting is made with the CS22:0 
(Clock Select) control bits. The setting is CS22:0 = 0x01. Set
CS20 to 1 in TCCR2B. CS20 is located at bit poition 0 in TCCR2B. 
*/
    TCCR2B |= 1; // binary 00000001




/*
Set the clock prescaler to "NO PRESCALING". With a 16MHz crystal,
TIMER2 is clocked at 16MHz. This setting is made with the CS22:0 
(Clock Select) control bits. The setting is CS22:0 = 0x01. Set
CS20 to 1 in TCCR2B. CS20 is located at bit poition 0 in TCCR2B. 
*/
    TCCR2B |= 1; // binary 00000001 works



/*
Enable OCF2A flag to generate the ISR(TIMER2_COMPA_vect){} interrupt on a match between TCNT2 and
OCR2A (the counter "TOP" value). To enable the OCF2A flag interrupt, enable the OCIE2A (Timer2
Output Compare Match A Enable) bit. The OCIE2A bit is located at bit position 1 in TMSK2.
*/
    TIMSK2 |= 2; // binary 00000010

    sei(); // set global interrupts enable

}

/*
The ISR name is particular to a particlar interrupt. You can't make up your own a name for
the function. The required names are found at nongnu.org in the AVR  superproject libc docs.
There's a big list of them along  with a list of which chips do a particular type of interrupt.
*/


ISR(TIMER2_COMPA_vect){ 
  
  outval ^= 1;          // toggle the pin
  digitalWrite(HF0, outval);

}


